//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<string.inl>
///	@path	~/src/lib/utilities/
///	@date	2007/06/18
///	@desc	.

#pragma once

#include "lib/utilities/string.h"

// use system conversion function
#include "config/os.h"

namespace xeres {

	// from Unicode to Multibyte
	static inline void wcs_to_mbs( const WString& src , AString& dst )
	{
		// get desired length
		int alen = ::WideCharToMultiByte( CP_THREAD_ACP , 0 ,
			src.c_str() , (int)src.size() ,
			0 , 0 ,
			0 , FALSE );

		// resize destination
		dst.resize( alen );

		// convert
		::WideCharToMultiByte( CP_THREAD_ACP , 0 ,
			src.c_str() , (int)src.size() ,
			const_cast<char*>( dst.c_str() ) , alen ,
			0 , FALSE );
	}

	// from Unicode to Multibyte
	static inline void wcs_to_mbs( const wchar_t * src , AString& dst )
	{
		size_t len = wcslen(src);
		// get desired length
		int alen = ::WideCharToMultiByte( CP_THREAD_ACP , 0 ,
			src , (int)len ,
			0 , 0 ,
			0 , FALSE );

		// resize destination
		dst.resize( alen );

		// convert
		::WideCharToMultiByte( CP_THREAD_ACP , 0 ,
			src , (int)len ,
			const_cast<char*>( dst.c_str() ) , alen ,
			0 , FALSE );
	}

	// from Multibyte to Unicode
	static inline void mbs_to_wcs( const AString& src , WString& dst )
	{
		// get desired length
		int ulen = ::MultiByteToWideChar( CP_THREAD_ACP , 0 ,
			src.c_str() , (int)src.size() ,
			0 , 0 );

		// resize destination
		dst.resize( ulen );

		// convert
		::MultiByteToWideChar( CP_THREAD_ACP , 0 ,
			src.c_str() , (int)src.size() ,
			const_cast<wchar_t*>( dst.c_str() ) , ulen );
	}

	static inline void mbs_to_wcs( const char * src , WString& dst )
	{
		size_t len = strlen(src);
		// get desired length
		int ulen = ::MultiByteToWideChar( CP_THREAD_ACP , 0 ,
			src , (int)len ,
			0 , 0 );

		// resize destination
		dst.resize( ulen );

		// convert
		::MultiByteToWideChar( CP_THREAD_ACP , 0 ,
			src , (int)len ,
			const_cast<wchar_t*>( dst.c_str() ) , ulen );
	}

	// break string into strings, with separator.
	template< typename _STLString > static inline void break_string(
		const _STLString& str , const wchar_t seperator ,
		std::deque< _STLString >& result , bool invert_seq )
	{
		// first position
		_STLString::size_type start = 0;
		_STLString::size_type end = str.find( seperator , start );
		_STLString::size_type total = str.size();

		// skip beginning strip of separator
		while( end == start )
		{
			start += 1;
			end = str.find( seperator , start );
		}

		if( end == _STLString::npos )
		{
			result.push_back( str.substr( start ) );
		}
		else
		{
			// strip start white space
			if( str[start] == ' ' )
			{
				for( ;; ++start )
				{
					if( str[start] == ' ' )
						continue;
					else
						break;
				}
				if( start >= total )
					return;

				end = str.find( seperator , start );

				while( start == end )
				{
					++start;
					end = str.find( seperator , start );

					if( end == _STLString::npos )
						return;
				}
			}

			// strip the last
			if( str[total-1] == ' ' )
			{
				while( str[total-1] == ' ' )
					--total;
			}

			// break string
			do
			{
				assert( end > start );
				_STLString sub( str.substr( start , end - start ) );
				if( !sub.empty() )
				{
					if( invert_seq )
						result.push_front( sub );
					else
						result.push_back( sub );
				}
				start = end + 1;
				end = str.find( seperator , start );
				while( start == end )
				{
					++start;
					end = str.find( seperator , start );
					if( end == _STLString::npos )
						break;
				}
			}while( end != _STLString::npos );

			if( end != start )
			{
				_STLString sub( str.substr( start , total - start ) );
				if( !sub.empty() )
				{
					if( invert_seq )
						result.push_front( sub );
					else
						result.push_back( sub );
				}
			}
		}
	}

#define	UTF8_MASKBITS            	(0x3F)
#define	UTF8_MASKBYTE            	(0x80)
#define	UTF8_MASK2BYTES          	(0xC0)
#define	UTF8_MASK3BYTES          	(0xE0)
#define	UTF8_MASK4BYTES          	(0xF0)
#define	UTF8_MASK5BYTES          	(0xF8)
#define	UTF8_MASK6BYTES          	(0xFC)

	/*!
		\brief		Encode string to UTF-8.
	*/
	static inline size_t encode_to_utf8( const wchar_t * src , size_t len , std::vector< byte >& output )
	{
		size_t i = 0;
		for( ; i < len ; ++i )
		{
			// 0xxxxxxx
			if( src[i] < 0x80 )
			{
				output.push_back( (byte)src[i] );
			}
			// 110xxxxx 10xxxxxx
			else if( src[i] < 0x800 )
			{
				output.push_back( (byte)(UTF8_MASK2BYTES | src[i] >> 6) );
				output.push_back( (byte)(UTF8_MASKBYTE | src[i] & UTF8_MASKBITS) );
			}
			// 1110xxxx 10xxxxxx 10xxxxxx
			else if( src[i] < 0x10000 )
			{
				output.push_back( (byte)(UTF8_MASK3BYTES | src[i] >> 12) );
				output.push_back( (byte)(UTF8_MASKBYTE | src[i] >> 6 & UTF8_MASKBITS) );
				output.push_back( (byte)(UTF8_MASKBYTE | src[i] & UTF8_MASKBITS) );
			}
		}
		return i;
	}

	/*!
		\brief		Decode string from UTF-8.
	*/
	static inline size_t decode_from_utf8( const byte * src , size_t len , WString& dst )
	{
		size_t i = 0;
		for( ; i < len ; )
		{
			wchar_t ch;

			// 1110xxxx 10xxxxxx 10xxxxxx
			if( ( src[i] & UTF8_MASK3BYTES ) == UTF8_MASK3BYTES )
			{
				ch = ( ( src[i] & 0x0F ) << 12 ) | ( ( src[i+1] & UTF8_MASKBITS ) << 6 ) | ( src[i+2] & UTF8_MASKBITS );
				i += 3;
			}
			// 110xxxxx 10xxxxxx
			else if( ( src[i] & UTF8_MASK2BYTES ) == UTF8_MASK2BYTES )
			{
				ch = ( ( src[i] & 0x1F ) << 6) | ( src[i+1] & UTF8_MASKBITS );
				i += 2;
			}
			// 0xxxxxxx
			else if( src[i] < UTF8_MASKBYTE )
			{
				ch = src[i];
				i += 1;
			}

			dst.push_back( ch );
		}
		return i;
	}

#undef UTF8_MASKBITS
#undef UTF8_MASKBYTE
#undef UTF8_MASK2BYTES
#undef UTF8_MASK3BYTES
#undef UTF8_MASK4BYTES
#undef UTF8_MASK5BYTES
#undef UTF8_MASK6BYTES

	static inline bool _isdigit( wchar_t ch )
	{
		return ch >= '0' && ch <= '9';
	}

	static inline int _xatoc( wchar_t c )
	{
		switch( c )
		{
		case '0': return 0; case '1': return 1;
		case '2': return 2; case '3': return 3;
		case '4': return 4; case '5': return 5;
		case '6': return 6; case '7': return 7;
		case '8': return 8; case '9': return 9;
		case 'a': case 'A' : return 0xa;
		case 'b': case 'B' : return 0xb;
		case 'c': case 'C' : return 0xc;
		case 'd': case 'D' : return 0xd;
		case 'e': case 'E' : return 0xe;
		case 'f': case 'F' : return 0xf;
		default:
			return -1;
		}
	}

	static inline int _xatoi( const wchar_t *& str )
	{
		str += 2;
		int res = 0;
		for( int i = 0 , cc = 0 ; i < 8 && ( cc = _xatoc( *(str++) ) ) != -1 ; ++i )
		{
			res = res << 4;
			res |= cc;
		}
		return res;
	}

	static inline int xstrtoi_scan( const wchar_t *& str )
	{
		assert( _isdigit(*str) || *str == '-' );
		if( str[0] == '0' && ( str[1] == 'x' || str[1] == 'X' ) )
		{
			return _xatoi( str );
		}
		else
		{

			int r = *str == '-' ? -(*((++str)++) - '0') : (*str++) - '0';
			while( *str && _isdigit(*str) )
				r = r * 10 + ( *str++ - '0' );
			return r;
		}
	}

	static inline double xstrtod_scan( const wchar_t *& s )
	{
		double r;		/* result */
		int e;			/* exponent */
		double d;		/* scale */
		int sign;		/* +- 1.0 */
		int esign;
		int i;
		int flags=0;

		r = 0.0;
		sign = 1;
		e = 0;
		esign = 1;

		while ((*s == ' ') || (*s == '\t'))
			s++;

		if (*s == '+')
			s++;
		else if (*s == '-')
		{
			sign = -1;
			s++;
		}

		while ((*s >= '0') && (*s <= '9'))
		{
			flags |= 1;
			r *= 10.0;
			r += *s - '0';
			s++;
		}

		if (*s == '.')
		{
			d = 0.1L;
			s++;
			while ((*s >= '0') && (*s <= '9'))
			{
				flags |= 2;
				r += d * (*s - '0');
				s++;
				d *= 0.1L;
			}
		}

		if (flags == 0)
		{
			return 0;
		}

		if ((*s == 'e') || (*s == 'E'))
		{
			s++;
			if (*s == '+')
				s++;
			else if (*s == '-')
			{
				s++;
				esign = -1;
			}
			if ((*s < '0') || (*s > '9'))
			{
				return r;
			}

			while ((*s >= '0') && (*s <= '9'))
			{
				e *= 10;
				e += *s - '0';
				s++;
			}
		}

		if (esign < 0)
			for (i = 1; i <= e; i++)
				r *= 0.1L;
		else
			for (i = 1; i <= e; i++)
				r *= 10.0;

		return r * sign;
	}

	static inline int xstrtoi( const wchar_t * str )
	{
		const wchar_t * ss = str;
		return xstrtoi_scan( ss );
	}

	static inline double xstrtod( const wchar_t * str )
	{
		const wchar_t * ss = str;
		return xstrtod_scan( ss );
	}

} // namespace xeres
